﻿/*
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/

Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.
*/
package com.jorritdevries.console
{
	import fl.core.UIComponent;
	import flash.display.DisplayObjectContainer;
	import flash.display.InteractiveObject;
	import flash.events.ContextMenuEvent;
	import flash.events.Event;
	import flash.ui.ContextMenu;
	import flash.ui.ContextMenuItem;
	
	[IconFile("UIConsole.png")]
	
	/**
	 * Signals the console has been resized.
	 * @eventType com.jorritdevries.console.ResizeEvent.RESIZE
	 */
	[Event(name="resize", type="com.jorritdevries.console.ResizeEvent")]
	
	/**
	 * The Console is a component that can be added to any project to provide
	 * a graphical interface to the nl.submarine.console.Console class as
	 * provided in the SubActionscript code base.
	 * 
	 * Through this class the user can interact with functions made public to the
	 * nl.submarine.console.Console instance, as well as read useful information
	 * regarding the Flash movie, such as rendering speed (frames per second) and
	 * memory usage.
	 * 
	 * @author jorrit@jorritdevries.com
	 */
	public class UIConsole extends UIComponent
	{		
		// UI-less console
		private var mConsole:Console;
		// Context menu item used to display or hide menu
		private var mContextMenuItem:ContextMenuItem;
		
		// UI management, including input
		private var mUIController:UIController;
		
		public function UIConsole()
		{
			mConsole = Console.getInstance();
			
			// Create right click menu item to provide easy access to the Console if
			// requested
			mContextMenuItem = new ContextMenuItem("Hide Console", true);
			mContextMenuItem.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, contextMenuItemSelectHandler);
			
			// Listen to when we are added to the stage (to add enter frame handler)
			addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
		}
		
		override protected function configUI():void
		{
			super.configUI();
			
			// UI management
			mUIController = new UIController(this);
			mUIController.setSize(UIController.MIN_WIDTH, UIController.MIN_HEIGHT);
		}
		
		override protected function draw():void
		{
			super.draw();
		}
		
		/**
		 * Adds a context menu item for opening and closing the console for the
		 * given object. If the object does not own a ContextMenu, it will be
		 * created on the object.
		 * 
		 * Note: custom context menu items cannot be added to the stage.
		 * @param object The InteractiveObject to add the context menu item to.
		 */
		public function addToContextMenu(object:InteractiveObject):void
		{
			if (object.contextMenu)
			{
				contextMenu = object.contextMenu;
			}
			else
			{
				object.contextMenu = new ContextMenu();
			}
			object.contextMenu.customItems.push(mContextMenuItem);
		}
		
		/**
		 * Removes the context menu item for opening and closing the console
		 * from the list of context menu items of the given object.
		 * @param object The InteractiveObject to remove the context menu item
		 * 	from.
		 */
		public function removeFromContextMenu(object:InteractiveObject):void
		{
			if (object.contextMenu)
			{
				for (var i:int = 0; i < object.contextMenu.customItems.length; ++i)
				{
					if (object.contextMenu.customItems[i] == mContextMenuItem)
					{
						object.contextMenu.customItems.splice(i, 1);
						return;
					}
				}
			}
		}
		
		/**
		 * 
		 */
		[Inspectable(defaultValue="Console")]
		public function set title(title:String):void
		{
			mUIController.setTitle(title);
		}
		
		public function get title():String
		{
			return mUIController.getTitle();
		}
		
		/**
		 * 
		 */
		[Inspectable(defaultValue=false)]
		public function set echoCommands(echo:Boolean):void
		{
			mConsole.setEchoCommands(echo);
		}
		
		public function get echoCommands():Boolean
		{
			return mConsole.getEchoCommands();
		}
		
		/**
		 * 
		 */
		[Inspectable(defaultValue=20)]
		public function set commandBufferSize(size:uint):void
		{
			mConsole.setCommandBufferSize(size);
		}
		
		public function get commandBufferSize():uint
		{
			return mConsole.getCommandBufferSize();
		}
		
		public function clearCommandBuffer():void
		{
			mConsole.clearCommandBuffer();
		}
		
		/** 
		 * Overrides the default behaviour of set visible of the Sprite class. Adds additional
		 * functionality to display the correct setting in the context menu.
		 */
		override public function set visible(visible:Boolean):void
		{
			super.visible = visible;
			mContextMenuItem.caption = visible ? "Hide Console" : "Show Console";
			
			if (stage)
			{
				if (visible)
				{
					stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
				}
				else
				{
					stage.removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
				}
			}
		}
		
		/**
		 * @see http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/DisplayObject.html#width
		 */
		override public function set width(width:Number):void
		{
			super.width = width;
			mUIController.setSize(width, mUIController.getHeight());
		}
		
		override public function get width():Number
		{
			return mUIController.getWidth();
		}
		
		/**
		 * @see http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/display/DisplayObject.html#height
		 */
		override public function set height(height:Number):void
		{
			super.height = height;
			mUIController.setSize(mUIController.getWidth(), height);
		}
		
		override public function get height():Number
		{
			return mUIController.getHeight();
		}
		
		override public function setSize(width:Number, height:Number):void
		{
			super.setSize(width, height);
			if (mUIController)
			{
				mUIController.setSize(width, height);
			}
		}
		
		/**
		 * Adds a command to the list of commands that will be parsed the next
		 * time Console.parseCommandQueue is called.
		 * @param command A String specifying the type of command including a 
		 * space-seperated argument list.
		 */
		public function sendCommand(command:String):void
		{
			mConsole.sendCommand(command);
		}
		
		/**
		 * Sends a command to the parser, resulting in immediate execution.
		 * @param command A String specifying the type of command including a 
		 * space-seperated argument list.
		 * @throws A CommandParseException if parsing the command fails.
		 */
		public function sendImmediateCommand(command:String):void
		{
			mConsole.sendImmediateCommand(command);
		}
		
		/**
		 * Adds a ConsoleItem to the list which operates on a data member of a class instance.
		 * <p>The rest arguments <code>rest</code> can be used as follows. If the item needs
		 * to call a Function, add only the function, which takes an Array as arguments, to the
		 * given parameter list. If the item needs to address a variable, three arguments need
		 * to be added to the parameter list in the following order:
		 * <pre>
		 * - Object to which the variable belongs
		 * - Name of the variable to address
		 * </pre>
		 * An enumeration of possible types is defined in ConsoleItemType.
		 * @param name The name of the item, must be unique.
		 * @param description A description for the item
		 * @param type An uint specifying the datatype of the variable to address
		 * @param ...rest Extra arguments, see details regarding the usage
		 * @throws An IllegalArgumentException when one the given arguments is invalid
		 * @throws A DuplicateItemException when a ConsoleItem with the given name
		 * has been added already
		 * @see nl.submarine.console.ConsoleItemType
		 */
		public function addItem(name:String, description:String, type:uint, ...rest):void
		{
			mConsole.addItem.call(mConsole, [name, description, type].concat(rest));
		}
		
		/** Removes the named ConsoleItem from the list of items. */
		public function removeItem(name:String):void
		{
			mConsole.removeItem(name);
		}
		
		/**
		 * Prints the specified text in the output window using the specified colour.
		 * @param message The String to print
		 * @param colour The colour to use for printing the String
		 * @param useTrace Flag indicating whether or not the message will displayed through the
		 * 	Flash IDE output window.
		 */
		public function print(message:String, colour:uint = 0x000000, useTrace:Boolean = false):void
		{
			mConsole.print(message, colour, useTrace);
		}
		
		// Called when the selects to display or hide the console using the context menu
		private function contextMenuItemSelectHandler(e:ContextMenuEvent):void
		{
			visible = !visible;
			mContextMenuItem.caption = visible ? "Hide Console" : "Show Console";
		}
		
		// Called when added to stage
		private function addedToStageHandler(e:Event):void
		{
			if (visible)
			{
				stage.addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			}
		}	
		
		// Called every frame to update rendering and memory statistics
		private function enterFrameHandler(e:Event):void
		{
			mUIController.updateStatistics();
			mConsole.parseCommandQueue();
		}
	}

}